#ifndef __M_DB_H__
#define __M_DB_H__

#include <mysql/mysql.h>
#include <mutex>
#include <cassert>
#include "util.hpp"


class user_table{
private:
    MYSQL *mysql;/*mysql操作句柄*/
    std::mutex mutex;/*互斥锁保护多线程对数据库的访问操作是互斥的*/
public:
    user_table(const std::string &host,
        const std::string &username,
        const std::string &password,
        const std::string &dbname,
        uint16_t port = 3306) {
        mysql = mysql_util::mysql_create(host, username, password, dbname, port);
        assert(mysql != nullptr);
    }
    ~user_table() {
        mysql_util::mysql_destroy(mysql);
    }
    /*注册时新增用户*/
    bool insert(Json::Value &user) {
        if(user["username"].isNull() || user["password"].isNull()){
            DLOG("please input username or password!");
            return false;
        }
        /*形成insert语句并执行*/
#define USER_INSERT "insert user values(null, '%s', password('%s'), 1000, 0, 0);"
        char sql[4096];
        sprintf(sql, USER_INSERT, user["username"].asCString(), user["password"].asCString());
        bool n = mysql_util::mysql_exec(mysql, sql);
        if(n == false){
            DLOG("mysql insert failed!");
            return false;
        }
        return true;
    }
    /*登陆验证，并返回详细信息*/
    bool login(Json::Value &user) {
        if(user["username"].isNull() || user["password"].isNull()){
            DLOG("please input username or password!");
            return false;
        }
#define USER_LOGIN "select id, score, total_count, win_count from user \
                            where username='%s' and password=password('%s');"
        char sql[4096];
        sprintf(sql, USER_LOGIN, user["username"].asCString(), user["password"].asCString());
        MYSQL_RES *res = nullptr;
        {
            /* mysql提供的query操作都是原子操作，只涉及query的不需要加锁，而select需要query和store两步才能取得结果，需要加锁*/
            std::unique_lock<std::mutex> lock(mutex);
            int ret = mysql_util::mysql_exec(mysql, sql);
            if(ret == false){
                DLOG("user login failed!");
                return false;
            }
            /*获取结果集*/
            res = mysql_store_result(mysql);
            if(res == nullptr){
                DLOG("have no user info!");
                return false;
            }
        }
        /*获取行数和具体数据*/
        int row_num = mysql_num_rows(res);
        if(row_num != 1){
            DLOG("user info is not unique!");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = std::stoi(row[0]);
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        /*释放结果集*/
        mysql_free_result(res);
        return true;
    }
    bool select_by_name(const std::string &name, Json::Value &user) {
#define USER_SELECT_NAME "select id, score, total_count, win_count from user where username='%s';"
        char sql[4096];
        sprintf(sql, USER_SELECT_NAME, name.c_str());
        MYSQL_RES * res = nullptr;
        {
            std::unique_lock<std::mutex> lock(mutex);
            int ret = mysql_util::mysql_exec(mysql, sql);
            if(ret == false){
                DLOG("user name failed!");
                return false;
            }
            res = mysql_store_result(mysql);
            if(res == nullptr){
                DLOG("have no user info!");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if(row_num != 1){
            DLOG("user info is not unique!");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = std::stoi(row[0]);
        user["username"] = name;
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }
    bool select_by_id(int id, Json::Value &user) {
#define USER_SELECT_ID "select username, score, total_count, win_count from user where id=%d;"
        char sql[4096];
        sprintf(sql, USER_SELECT_ID, id);
        MYSQL_RES *res = nullptr;
        {
            std::unique_lock<std::mutex> lock(mutex);
            int ret = mysql_util::mysql_exec(mysql, sql);
            if(ret == false){
                DLOG("user id failed!");
                return false;
            }
            res = mysql_store_result(mysql);
            if(res == nullptr){
                DLOG("have no user info!");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if(row_num != 1){
            DLOG("user info is not unique!");
            return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = id;
        user["username"] = row[0];
        user["score"] = std::stoi(row[1]);
        user["total_count"] = std::stoi(row[2]);
        user["win_count"] = std::stoi(row[3]);
        mysql_free_result(res);
        return true;
    }
    /*胜利时，分数增加，总场次增加，胜利场次增加*/
    bool win(int id) {
#define USER_WIN "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 \
                where id=%d;"
        char sql[4096];
        sprintf(sql, USER_WIN, id);
        bool ret = mysql_util::mysql_exec(mysql, sql);
        if (ret == false){
            DLOG("update user win info failed!");
            return false;
        }
        return true;
    }
    /*输时，分数减少，总场次增加*/
    bool lose(int id) {
#define USER_LOSS "update user set score=score-30, total_count=total_count+1 where id=%d;"
        char sql[4096];
        sprintf(sql, USER_LOSS, id);
        bool ret = mysql_util::mysql_exec(mysql, sql);
        if(ret == false){
            DLOG("update user loss info failed!");
            return false;
        }
        return true;
    }
};

#endif
